using System;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms.VisualStyles;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing.Design;

namespace AdvancedDataGridView
{
	/// <summary>
	/// Summary description for TreeGridView.
	/// </summary>
    [Designer(typeof(System.Windows.Forms.Design.ControlDesigner)),
	ComplexBindingProperties(),
    Docking(DockingBehavior.Ask)]
	public class TreeGridView:DataGridView
	{		
		//public event ExpandingEventHandler RowExpanding;
		//private int _indentWidth;
		private TreeGridNode _root;
		private TreeGridColumn _expandableColumn;
		private bool _disposing = false;
		internal ImageList _imageList;
		private bool _inExpandCollapse = false;
		private Control hideScrollBarControl;

		internal VisualStyleRenderer rOpen = null;
		internal VisualStyleRenderer rClosed = null; 

        public TreeGridView()
		{
			// Control when edit occurs because edit mode shouldn't start when expanding/collapsing
			this.EditMode = DataGridViewEditMode.EditProgrammatically;

			//TODO: remove this when bug is fixed.
			this.RowHeadersVisible = false;

			//this.DoubleBuffered = true;

			// This sample does not support adding or deleting rows by the user.
			this.AllowUserToAddRows = false;
			this.AllowUserToDeleteRows = false;
			this._root = new TreeGridNode(this);
			this._root.IsRoot = true;


            // Make sure the visual styles are enabled before 
            // going any further.
            if (Application.RenderWithVisualStyles)
            {
                rOpen = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
                rClosed = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
            }
            else
            {
                /* 
                 * 
                 e.Graphics.DrawString("+",
                listView1.Font, Brushes.Red, e.Bounds, sf);
                 */
            }


			// Ensures that all rows are added unshared by listening to the CollectionChanged event.
			this.Rows.CollectionChanged += delegate(object sender, System.ComponentModel.CollectionChangeEventArgs e){};
		}

		protected override void OnRowsAdded(DataGridViewRowsAddedEventArgs e)
		{
			base.OnRowsAdded(e);
			// Notify the row when it is added to the base grid 
			int count = e.RowCount - 1;
			TreeGridNode row;
			while (count >= 0)
			{
				row = this.Rows[e.RowIndex + count] as TreeGridNode;
				if (row != null)
				{
					row.Sited();
				}
				count--;
			}
		}

		#region Ensure Full Row Select
		protected override void OnRowEnter(DataGridViewCellEventArgs e)
		{
			base.OnRowEnter(e);
			if (this.SelectionMode == DataGridViewSelectionMode.CellSelect ||
				(this.SelectionMode == DataGridViewSelectionMode.FullRowSelect &&
				this.Rows[e.RowIndex].Selected == false))
			{
				this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
				this.Rows[e.RowIndex].Selected = true;
			}
		}
	
		//protected override void OnCurrentCellChanged(EventArgs e)
		//{
		//    base.OnCurrentCellChanged(e);
		//    if (this.CurrentCellAddress.Y == -1) return;
		//    if (this.SelectionMode == DataGridViewSelectionMode.CellSelect ||
		//        (this.SelectionMode == DataGridViewSelectionMode.FullRowSelect &&
		//        this.BaseRows[this.CurrentCellAddress.Y].Selected == false))
		//    {
		//        this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
		//        if (this.CurrentCellAddress.X >= -1 && this.CurrentCellAddress.Y > -1)
		//            this.CurrentCell.OwningRow.Selected = true;
		//    }
		//}
#endregion

		protected override void OnHandleCreated(EventArgs e)
		{
			base.OnHandleCreated(e);
			hideScrollBarControl = new Control();
			hideScrollBarControl.Visible = false;
			hideScrollBarControl.Enabled = false;
			hideScrollBarControl.TabStop = false;
			this.Controls.Add(hideScrollBarControl);
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			// Cause edit mode to begin since edit mode is disabled to support 
			// expanding/collapsing 
			base.OnKeyDown(e);
			if (!e.Handled)
			{
				if (e.KeyCode == Keys.F2 && this.CurrentCellAddress.X > -1 && this.CurrentCellAddress.Y >-1)
				{
					if (!this.CurrentCell.Displayed)
					{
						this.FirstDisplayedScrollingRowIndex = this.CurrentCellAddress.Y;
					}
					else
					{
						// TODO:calculate if the cell is partially offscreen and if so scroll into view
					}
					this.SelectionMode = DataGridViewSelectionMode.CellSelect;
					this.BeginEdit(true);
				}
				else if (e.KeyCode == Keys.Enter && !this.IsCurrentCellInEditMode)
				{
					this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
					this.CurrentCell.OwningRow.Selected = true;
				}
			}
		}

		//protected override bool ProcessKeyEventArgs(ref Message m)
		//{
		//    // Cause edit mode to begin since edit mode is disabled to support 
		//    // expanding/collapsing 
		//    bool r = base.ProcessKeyEventArgs(ref m);
		//    if (!r)
		//    {
		//        if (m.Msg == EditingControlHelper.WM_SYSKEYDOWN || m.Msg == EditingControlHelper.WM_KEYDOWN)
		//        {
		//            if (this.CurrentCellAddress.X != -1)
		//            {
		//                DataGridViewCell dataGridViewCell = this.CurrentCell;

		//                Type editControlType = dataGridViewCell.EditType;
		//                Type editingCellInterface = null;
		//                if (editControlType == null)
		//                {
		//                    // Current cell does not have an editing control. Does it implement IDataGridViewEditingCell?
		//                    editingCellInterface = dataGridViewCell.GetType().GetInterface("IDataGridViewEditingCell");
		//                }

		//                KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);

		//                if ((editControlType != null || editingCellInterface == null || ke.KeyCode == Keys.F2) &&
		//                    dataGridViewCell.KeyEntersEditMode(ke))
		//                {
		//                    // Cell wants to go to edit mode
		//                    this.FirstDisplayedScrollingRowIndex = this.CurrentCellAddress.Y;
		//                    if (this.BeginEdit(!(ke.KeyCode == Keys.F2 && ModifierKeys == 0)))
		//                    {
		//                        // Forward the key message to the editing control if any
		//                        if (this.EditingControl != null)
		//                        {
		//                            EditingControlHelper.SendMessage(new System.Runtime.InteropServices.HandleRef(this, Handle), m.Msg, m.WParam, m.LParam);
		//                            return true;
		//                        }
		//                    }
		//                }
		//            }
		//        }
		//    }
		//    return r;
		//}
		/*
		 * 
		 * 
		 * 

		 * 
		 * 
		 * 
		 * */
		
		protected override void OnColumnAdded(DataGridViewColumnEventArgs e)
		{
			if (typeof(TreeGridColumn).IsAssignableFrom(e.Column.GetType())){
				// identify the expanding column.			
				_expandableColumn = (TreeGridColumn)e.Column;
			}

			// Expandable Grid doesn't support sorting
			e.Column.SortMode = DataGridViewColumnSortMode.NotSortable;
			if (_expandableColumn != null)
			{
				if (base.Columns.Count > 1)
				{
					_expandableColumn.Resizable = DataGridViewTriState.NotSet;
				}
				else
				{
					_expandableColumn.Resizable = DataGridViewTriState.False;
				}
			}
			base.OnColumnAdded(e);			
		}

		protected override void OnResize(EventArgs e)
		{
			SizeExpandableColumn();
			base.OnResize(e);
		}
		//protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
		//{
		//    base.SetBoundsCore(x, y, width, height, specified);
		//    SizeExpandableColumn();
		//}

		protected virtual void SizeExpandableColumn()
		{
			if (_disposing) return;
			if (_inExpandCollapse) return;
			if (base.Columns.Count == 1 && _expandableColumn != null)
			{
				int pWidth = _expandableColumn.GetPreferredWidth(DataGridViewAutoSizeColumnMode.AllCells, false);
				int pClientWidth;
				if (VerticalScrollBar.Visible)
				{
					pClientWidth = this.ClientRectangle.Width - VerticalScrollBar.Width - 3;
				}
				else
				{
					pClientWidth = this.ClientRectangle.Width - 3;
				}

				// when there is only one column the column width is based upon the preferred size
				if (pWidth > pClientWidth)
				{
					_expandableColumn.Width = pWidth;
				}
				else
				{
					_expandableColumn.Width = pClientWidth;

				}
			}
		}

		protected override void OnCellValueChanged(DataGridViewCellEventArgs e)
		{
			SizeExpandableColumn();
			base.OnCellValueChanged(e);
		}

		[Browsable(false),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
		EditorBrowsable(EditorBrowsableState.Never)]
		public new object DataSource
		{
			get { return null; }
			set { throw new NotSupportedException("The TreeGridView does not support databinding"); }
		}

		[Browsable(false),
	    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
		EditorBrowsable(EditorBrowsableState.Never)]
		public new object DataMember
		{
			get { return null; }
			set { throw new NotSupportedException("The TreeGridView does not support databinding"); }
		}

		// Shadow and hide the DGV properties so that we always go through our rows collection
		
		[Category("Data"),
		Description("The collection of root nodes in the treelist."),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		Editor(typeof(CollectionEditor), typeof(UITypeEditor))]
		public TreeGridNodeCollection Nodes
		{
			get
			{
				return this._root.Nodes;
			}
		}

		public new TreeGridNode CurrentRow
		{
			get
			{
				return base.CurrentRow as TreeGridNode;
			}
		}
		public TreeGridNode CurrentNode
		{
			get
			{
				return this.CurrentRow;
			}
		}

		public ImageLayout NodeImageLayout
		{
			get { return ImageLayout.Tile; }
			set { }
		}

		public Size NodeImageSize
		{
			get { return Size.Empty; }
			set { }
		}

		public ImageList ImageList
		{
			get { return this._imageList; }
			set { 
				this._imageList = value; 
				//TODO: should we invalidate cell styles when setting the image list?
			
			}
		}
				
		protected override void Dispose(bool disposing)
		{
			this._disposing = true;
			base.Dispose(Disposing);
			this.UnSiteAll();
		}
		public new int RowCount
		{
			get { return this.Nodes.Count; }
			set
			{
				for (int i = 0; i < value; i++)
					this.Nodes.Add(new TreeGridNode());

			}
		}

		internal protected void UnSiteAll()
		{
			this.UnSiteNode(this._root);
		}

		internal protected virtual void UnSiteNode(TreeGridNode node)
		{
			//Debug.Assert(row.Parent != null && row.Parent.IsExpanded == true);
			if (node.IsSited || node.IsRoot)
			{
				// remove child rows first
				foreach (TreeGridNode childNode in node.Nodes)
				{
					this.UnSiteNode(childNode);
				}

				// now remove this row except for the root
				if (!node.IsRoot)
				{
					this.Rows.Remove(node);
					// Row isn't sited in the grid anymore after remove. Note that we cannot
					// Use the RowRemoved event since we cannot map from the row index to
					// the index of the expandable row/node.
					node.UnSited();
				}
			}
			SizeExpandableColumn();
		}

		internal protected virtual bool CollapseNode(TreeGridNode node)
		{
			if (node.IsExpanded)
			{
				CollapsingEventArgs exp = new CollapsingEventArgs(node);
				this.OnNodeCollapsing(exp);

				if (!exp.Cancel)
				{
					_inExpandCollapse = true;
					this.LockVerticalScrollBarUpdate(true);
					node.IsExpanded = false;

					foreach (TreeGridNode childNode in node.Nodes)
					{
						Debug.Assert(childNode.RowIndex != -1, "Row is NOT in the grid.");
						this.UnSiteNode(childNode);
					}

					CollapsedEventArgs exped = new CollapsedEventArgs(node);
					this.OnNodeCollapsed(exped);
					//TODO: Convert this to a specific NodeCell property
					_inExpandCollapse = false;
					this.LockVerticalScrollBarUpdate(false);
					this.InvalidateCell(node.Cells[0]);
					this.SizeExpandableColumn();

				}

				return exp.Cancel;
			}
			else
			{
				// row isn't expanded, so we didn't do anything.				
				return false;
			}
		}

		internal protected virtual void SiteNode(TreeGridNode node)
		{
			//TODO: Raise exception if parent node is not the root or is not sited.
			int rowIndex = -1;
			TreeGridNode currentRow;
			//int rowLevelCount;
			node._grid = this;

			if (node.Parent != null && node.Parent.IsRoot == false)
			{
				// row is a child
				Debug.Assert(node.Parent != null && node.Parent.IsExpanded == true);

				if (node.Index > 0)
				{
					currentRow = node.Parent.Nodes[node.Index - 1];
				}
				else
				{
					currentRow = node.Parent;
				}
			}
			else
			{
				// row is being added to the root
				if (node.Index > 0)
				{
					currentRow = node.Parent.Nodes[node.Index - 1];
				}
				else
				{
					currentRow = null;
				}

			}

			if (currentRow != null)
			{
				while (currentRow.Level >= node.Level)
				{
					if (currentRow.RowIndex < this.Rows.Count - 1)
					{
						currentRow = this.Rows[currentRow.RowIndex + 1] as TreeGridNode;
						Debug.Assert(currentRow != null);
					}
					else
						// no more rows, site this node at the end.
						break;

				}
				if (currentRow == node.Parent)
					rowIndex = currentRow.RowIndex + 1;
				else if (currentRow.Level < node.Level)
					rowIndex = currentRow.RowIndex;
				else
					rowIndex = currentRow.RowIndex + 1;
			}
			else
				rowIndex = 0;


			Debug.Assert(rowIndex != -1);
			this.SiteNode(node, rowIndex);

			Debug.Assert(node.IsSited);
			if (node.IsExpanded)
			{
				// add all child rows to display
				foreach (TreeGridNode childNode in node.Nodes)
				{
					//TODO: could use the more efficient SiteRow with index.
					this.SiteNode(childNode);
				}
			}
			SizeExpandableColumn();
		}


		internal protected virtual void SiteNode(TreeGridNode node, int index)
		{
			if (index < this.Rows.Count)
			{
				this.Rows.Insert(index, node);
			}
			else
			{
				// for the last item.
				this.Rows.Add(node);
			}
		}

		internal protected virtual bool ExpandNode(TreeGridNode node)
		{
			if (!node.IsExpanded)
			{
				ExpandingEventArgs exp = new ExpandingEventArgs(node);
				this.OnNodeExpanding(exp);

				if (!exp.Cancel)
				{
					_inExpandCollapse = true;
					this.LockVerticalScrollBarUpdate(true);
					node.IsExpanded = true;

					//TODO Convert this to a InsertRange
					foreach (TreeGridNode childNode in node.Nodes)
					{
						Debug.Assert(childNode.RowIndex == -1, "Row is already in the grid.");

						this.SiteNode(childNode);
						//this.BaseRows.Insert(rowIndex + 1, childRow);
						//TODO : remove -- just a test.
						// childNode.Cells[0].Value = "child";
					}

					ExpandedEventArgs exped = new ExpandedEventArgs(node);
					this.OnNodeExpanded(exped);
					//TODO: Convert this to a specific NodeCell property
					_inExpandCollapse = false;
					this.LockVerticalScrollBarUpdate(false);
					this.InvalidateCell(node.Cells[0]);
					this.SizeExpandableColumn();
				}

				return exp.Cancel;
			}
			else
			{
				// row is already expanded, so we didn't do anything.
				return false;
			}
		}

		protected virtual void OnNodeExpanding(ExpandingEventArgs e)
		{
		}
		protected virtual void OnNodeExpanded(ExpandedEventArgs e)
		{
		}
		protected virtual void OnNodeCollapsing(CollapsingEventArgs e)
		{
		}
		protected virtual void OnNodeCollapsed(CollapsedEventArgs e)
		{
		}

		private void LockVerticalScrollBarUpdate(bool lockUpdate/*, bool delayed*/)
		{
			// LockVerticalScrollBarUpdate call WM_SETREDRAW( FALSE/TRUE)
			if (this.VerticalScrollBar.IsHandleCreated)
			{
				this.SuspendLayout();
				if (lockUpdate) {
					this.VerticalScrollBar.Parent = hideScrollBarControl;
				} else {
					this.VerticalScrollBar.Parent = this;
				}
				this.ResumeLayout();
				//    Win32Helper.SendMessage(new System.Runtime.InteropServices.HandleRef(this, this.VerticalScrollBar.Handle), Win32Helper.WM_SETREDRAW, 0, 0);
				////if (delayed)
				////    Win32Helper.PostMessage(new HandleRef(this, Handle), NativeMethods.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
				//else
				//    Win32Helper.SendMessage(new System.Runtime.InteropServices.HandleRef(this, this.VerticalScrollBar.Handle), Win32Helper.WM_SETREDRAW, 1, 0);
			}
		}
		//private static class Win32Helper {
		//    public const int WM_SYSKEYDOWN = 0x0104,
		//                     WM_KEYDOWN = 0x0100,
		//                     WM_SETREDRAW = 0x000B;

		//    [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
		//    public static extern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);

		//    [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
		//    public static extern IntPtr SendMessage(System.Runtime.InteropServices.HandleRef hWnd, int msg, int wParam, int lParam);

		//    [System.Runtime.InteropServices.DllImport("USER32.DLL", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
		//    public static extern bool PostMessage(System.Runtime.InteropServices.HandleRef hwnd, int msg, IntPtr wparam, IntPtr lparam);

		//}
	}

}
